实现 User 微服务健康检查:gRPCHealth 完整集成
本节在共享 proto 项目中完成 health.proto 的编译后,将 gRPC 健康检查集成到 User 微服务中,包括模块创建、装饰器配置、类型导入和测试验证。
前置准备:构建共享 Proto 项目
由于 proto 共享项目引用的是 dist 目录中的文件,需要先构建:
cd proto-pkg
pnpm build
bash
创建 Health 模块
使用 NestJS CLI 创建 Health 模块:
cd user
nest g mo health
nest g co health
bash
这会创建以下文件并自动更新 app.module.ts:
health/health.module.tshealth/health.controller.tshealth/health.controller.spec.ts
删除不需要的测试文件。
使用 Health Check 装饰器
在 health.controller.ts 中使用 ts-proto 生成的装饰器方法:
// health/health.controller.ts
import { Controller } from '@nestjs/common'
import { HealthServiceControllerMethods } from '@toimc/proto-pkg'
@Controller('health')
@HealthServiceControllerMethods()
export class HealthController {
check(data: HealthCheckRequest): HealthCheckResponse {
console.log('health check data:', data)
return { status: 1 } // 1 = SERVING
}
}
typescript
类型导入问题排查
问题一:导出路径不正确
检查 dist 目录中的 index.d.ts,确认 health 相关类型是否正确导出。
如果 index.ts 中导入路径包含了多余的 proto 目录层级,需要调整:
// 错误:路径中多了 proto 目录
export * from './proto/user'
// 正确:直接导入编译输出目录
export * from './user'
typescript
问题二:使用 Namespace 分离导出
当 health 和 user 的类型存在冲突时,可以使用 namespace 方式导出:
// index.ts
import * as health from './health'
import * as user from './user'
export { health, user }
typescript
但这种方式会让所有类型都需要通过 namespace 访问(如 user.HealthCheckRequest),增加了使用复杂度。
问题三:单独打包 Health 模块(推荐方案)
将 health 相关文件单独打包,避免与 user 的类型冲突。
在 proto-pkg 的 package.json 中添加独立的导出入口:
{
"exports": {
".": "./dist/index.js",
"./health": "./dist/health/index.js"
}
}
json
添加独立的构建脚本:
{
"scripts": {
"build": "tsup index.ts",
"build:health": "tsup health/index.ts --outDir dist/health"
}
}
json
TypeScript 模块解析配置
如果遇到模块解析错误,需要检查 tsconfig.json 中的 moduleResolution 设置:
node16或bundler模式支持 package.json 中的 exports 字段node(CommonJS)模式不支持 exports,需要直接指向文件路径
调整导入路径以匹配实际的文件结构:
// 根据模块解析模式选择合适的导入方式
import { HealthCheckRequest, HealthCheckResponse } from '@toimc/proto-pkg/health'
typescript
实现健康检查端点
// health/health.controller.ts
import { Controller } from '@nestjs/common'
import {
HealthServiceControllerMethods,
HealthCheckRequest,
HealthCheckResponse
} from '@toimc/proto-pkg'
@Controller('health')
@HealthServiceControllerMethods()
export class HealthController {
check(data: HealthCheckRequest): HealthCheckResponse {
console.log('health check request:', data)
return { status: 1 }
}
}
typescript
HealthCheckResponse 的 status 字段值:
| 值 | 常量 | 含义 |
|---|---|---|
| 0 | UNKNOWN | 未知状态 |
| 1 | SERVING | 正常服务中 |
| 2 | NOT_SERVING | 服务不可用 |
| 3 | SERVICE_UNKNOWN | 服务未知 |
使用 grpcurl 测试
# 进入 proto 目录
cd proto-pkg/src/proto
# 测试健康检查
grpcurl \
-plaintext \
-import-path . \
-proto user.proto \
-d '{"service": "user_service"}' \
localhost:4001 \
user.HealthService/Check
bash
注意参数:
-import-path:proto 文件所在目录-proto:指定 proto 文件名-d:传递 JSON 参数,字段名要与 proto 定义一致
替代方案:使用 @GrpcMethod 装饰器
如果不想使用 ts-proto 生成的装饰器,可以直接使用 NestJS 内置的 @GrpcMethod:
// health/health.controller.ts
import { Controller } from '@nestjs/common'
import { GrpcMethod } from '@nestjs/microservices'
@Controller('health')
export class HealthController {
@GrpcMethod('HealthService', 'Check')
check(data: any) {
console.log('health check:', data)
return { status: 1 }
}
}
typescript
这种方式更简单直接,不需要处理复杂的类型导出和模块打包问题。两种方式的效果完全一致,选择哪种取决于团队偏好。
Health 模块注册
确保 HealthModule 在 User 微服务的 app.module.ts 中注册:
// app.module.ts
import { Module } from '@nestjs/common'
import { HealthModule } from './health/health.module'
@Module({
imports: [
// 其他模块...
HealthModule
]
})
export class AppModule {}
typescript
完整流程总结
- 构建共享 proto 项目(
pnpm build) - 使用 NestJS CLI 创建 Health 模块
- 选择装饰器方式(
@HealthServiceControllerMethods或@GrpcMethod) - 处理类型导入路径问题
- 实现 check 方法,返回 SERVING 状态
- 使用 grpcurl 验证健康检查端点
- 后续将健康检查对接到 Consul 注册中心
下一步
健康检查端点创建完成后,下一步是将 Consul 服务注册与健康检查对接,实现微服务的自动注册和状态监控。
↑